﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using KillParser;

namespace Killboard.Models
{
    public class KillMail
    {
        #region properties

        //private ekbKill kill = new ekbKill();

        //public ekbKill Kill
        //{
        //    get { return kill; }
        //    set { kill = value; }
        //}

        private DateTime killTimeUTC;

        public DateTime KillTimeUTC
        {
            get { return killTimeUTC; }
            set { killTimeUTC = value; }
        }

        private string originalKillText;

        public string OriginalKillText
        {
            get { return originalKillText; }
        }

        private Pilot victim = new Pilot();

        public Pilot Victim
        {
            get { return victim; }
        }

        private List<Pilot> involved = new List<Pilot>();

        public List<Pilot> Involved
        {
            get { return involved; }
        }

        private StarSystem fightSystem = new StarSystem();

        public StarSystem FightSystem
        {
            get { return fightSystem; }
        }

        private List<Item> itemCasualties = new List<Item>();

        public List<Item> ItemCasualties
        {
            get { return itemCasualties; }
        }

        private List<string> itemsInError = new List<string>();

        #endregion

        private bool goodMail = false;

        public bool GoodMail
        {
            get { return goodMail; }
            
        }

        public KillMail() { }

        public void Parse(string killText)
        {
            originalKillText = killText;

            try
            {
                goodMail = true;
                Parse();
            }
            catch (Exception e)
            {
                goodMail = false;
                itemsInError.Add(e.ToString());
            }

        }

        public void Parse(FileInfo killFile)
        {
            using (TextReader tr = killFile.OpenText())
            {
                Parse(tr);
            }
        }

        public void Parse(TextReader tr)
        {
            string killText = tr.ReadToEnd();
            Parse(killText);
        }

        public void Parse()
        {
            if (originalKillText == null || originalKillText.Trim().Length == 0)
            {
                throw new BadKillException("Kill text is empty.");
            }

            StringReader sr = new StringReader(originalKillText);

            ParseDate(sr);
            ParseVictim(sr);
            ParseInvolved(sr);

            sr.Close();
            sr = new StringReader(originalKillText);
            ParseItems(sr, "dropped items:");

            sr.Close();
            sr = new StringReader(originalKillText);
            ParseItems(sr, "destroyed items:");

        }



        private void ParseVictim(StringReader sr)
        {
            string inputLine = sr.ReadLine();

            Dictionary<string, string> VictimInfo = new Dictionary<string, string>();

            while (inputLine != null && !inputLine.Trim().Equals("Involved parties:", StringComparison.CurrentCultureIgnoreCase))
            {
                if (inputLine.Trim().Length == 0)
                {
                    inputLine = sr.ReadLine();
                    continue;
                }

                string[] splitz = inputLine.Split(':');
                if (splitz.Length != 2)
                {
                    throw new BadKillException("Victim info has an incorrect name/value pair");
                }

                string key = splitz[0].Trim().ToLower();
                string value = splitz[1].Trim();

                VictimInfo.Add(key, value);

                inputLine = sr.ReadLine();
            }

            victim.Name = VictimInfo["victim"];
            victim.CorpName = VictimInfo["corp"];
            victim.Alliance = VictimInfo["alliance"];
            victim.Faction = VictimInfo["faction"];
            victim.Ship = VictimInfo["destroyed"];
            victim.DamageTakenOrDone = Convert.ToInt32(VictimInfo["damage taken"]);

            fightSystem.Name = VictimInfo["system"];
            fightSystem.SecStatus = (float)Convert.ToDouble(VictimInfo["security"]);



        }

        private void ParseDate(StringReader sr)
        {
            string inputLine = sr.ReadLine();

            try
            {
                KillTimeUTC = DateTime.ParseExact(inputLine, "yyyy.MM.dd HH:mm:ss", null);
            }
            catch (Exception e)
            {
                throw new BadKillException("Bad Killdate", e);
            }
                
        }

        private void ParseInvolved(StringReader sr)
        {
            //TODO: Need to determine how to figure out if the involved was an NPC

            string inputLine;

            List<Dictionary<string, string>> involvedRaw = new List<Dictionary<string, string>>();
            Dictionary<string, string> rawData = new Dictionary<string,string>();


            while((inputLine = sr.ReadLine()) != null && 
                !inputLine.Trim().Equals("dropped items:", StringComparison.CurrentCultureIgnoreCase) &&
                !inputLine.Trim().Equals("destroyed items:", StringComparison.CurrentCultureIgnoreCase))
            {

                if (inputLine.Trim().Length == 0)
                {
                    if (rawData.Count > 0)
                    {
                        involvedRaw.Add(rawData);
                        rawData = new Dictionary<string, string>();
                    }
                    continue;
                }

                string[] splitz = inputLine.Split(':');
                if (splitz.Length != 2)
                {
                    throw new BadKillException("Involved info has a malformed name/value pair");
                }

                string key = splitz[0].Trim().ToLower();
                string value = splitz[1].Trim();

                rawData.Add(key, value);

            }

            foreach (Dictionary<string, string> raw in involvedRaw)
            {
                
                Pilot killer = new Pilot();

                

                killer.Alliance = GetSafeString(raw , "alliance");
                killer.CorpName = GetSafeString(raw, "corp");
                killer.DamageTakenOrDone = Convert.ToInt32(GetSafeString(raw ,"damage done"));
                killer.Faction = GetSafeString(raw, "faction");
                killer.SecStatus = (float)Convert.ToDouble(GetSafeString(raw, "security"));
                killer.Ship = GetSafeString(raw, "ship");
                killer.Weapon = GetSafeString(raw, "weapon");

                string name = raw["name"];
                if (name.Contains("(laid the final blow)"))
                {
                    name = name.Replace("(laid the final blow)", "");
                    killer.LaidTheFinalBlow = true;
                }

                killer.Name = name;

                if (killer.Name.Contains('/'))
                {
                    string[] npcSplit = killer.Name.Split('/');
                    for (int i = 0; i < npcSplit.Length; i++)
                    {
                        npcSplit[i] = npcSplit[i].Trim();
                    }

                    killer.Name = npcSplit[0];
                    killer.CorpName = npcSplit[1];
                    
                }   

                int? npcID = new KillRepository().GetNPCID(killer.Name);

                if (npcID != null)
                {
                    killer.IsNPC = true;
                    killer.Weapon = killer.Name;
                }


                Involved.Add(killer);
            }
        }

        private string GetSafeString(Dictionary<string, string> raw, string p)
        {
            if (raw.ContainsKey(p))
            {
                return raw[p];
            }
            else
            {
                return null;
            }
        }

        private void ParseItems(StringReader sr, string itemType)
        {
            
            string inputLine;

            //seek to itemType

            while ((inputLine = sr.ReadLine()) != null && !inputLine.Equals(itemType, StringComparison.CurrentCultureIgnoreCase)) ;

            if (inputLine == null)
            {
                return;
            }

            KillRepository repo = new KillRepository();


            while ((inputLine = sr.ReadLine()) != null &&
                !inputLine.Equals("dropped items:", StringComparison.CurrentCultureIgnoreCase) &&
                !inputLine.Equals("destroyed items:", StringComparison.CurrentCultureIgnoreCase))
            {
                if (inputLine.Trim().Length == 0)
                {
                    //skip blanks
                    continue;
                }
                Item i = ParseSingleItem(inputLine);
                if (itemType == "dropped items:")
                {
                    i.WasDestroyed = false;
                }
                else
                {
                    i.WasDestroyed = true;
                }

                invType eveItem = repo.FindItem(i.ItemName);

                if (eveItem == null)
                {
                    itemsInError.Add(string.Format("Item {0} was not found.", i.ItemName));
                }
                else
                {
                    i.DbItem = eveItem;
                    itemCasualties.Add(i);
                }
               
            }


            
        }

        private Item ParseSingleItem(string inputLine)
        {
            Item i = new Item();

            i.InCargo = false;
            if (inputLine.Contains("(Cargo"))
            {
                inputLine = inputLine.Replace("(Cargo)", "");
                i.InCargo = true;
            }

            if(inputLine.Contains("(Drone Bay)"))
            {
                inputLine = inputLine.Replace("(Drone Bay)","");
                i.InCargo = true;
            }

            if (inputLine.Contains("(In Container)"))
            {
                inputLine = inputLine.Replace("(In Container)", "");
                i.InCargo = true;
            }

            string[] splitz = inputLine.Split(',');

            if (splitz.Length > 1)
            {
                //got quantity
                string qty = splitz[1].ToLower().Replace("qty:", "").Trim();
                i.ItemQty = Convert.ToInt16(qty);
            }
            else
            {
                i.ItemQty = 1;
            }

             i.ItemName = splitz[0];

            return i;
        }

        public int SaveKill()
        {
            if (!goodMail)
            {
                throw new CannotSaveException("Killmail is malformed.");
            }

            KillRepository repo = new KillRepository();

            ekbPlayerCorp corp = repo.FindPlayerCorp(victim.CorpName);
            if (corp == null)
            {
                corp = repo.FindNPCCorp(victim.CorpName);
            }

            if (corp == null)
            {
                corp = repo.AddPlayerCorp(victim.CorpName, victim.Alliance);
            }

            ekbAlliance alliance = repo.FindAlliance(victim.Alliance);

            victim.CorpID = corp.__ID;

            if (alliance == null)
            {
                victim.AllianceID = null;
            }
            else
            {
                victim.AllianceID = alliance.__ID;
            }


            int? victimPilot = repo.FindPilot(victim.Name);
            if(victimPilot == null)
            {
                victimPilot = repo.AddPilot(victim.Name, victim.PilotID);
            }
            victim.PilotID = victimPilot;


            victim.ShipID = repo.FindItem(victim.Ship).typeID;

            foreach (Pilot killer in involved)
            {

                if (!killer.IsNPC)
                {
                    corp = repo.FindPlayerCorp(killer.CorpName);
                    if (corp == null)
                    {
                        corp = repo.FindNPCCorp(killer.CorpName);
                    }
                    if (corp == null)
                    {
                        corp = repo.AddPlayerCorp(killer.CorpName, killer.Alliance);
                    }

                    killer.PilotID = repo.FindPilot(killer.Name);
                    if (killer.PilotID == null)
                    {
                        killer.PilotID = repo.AddPilot(killer.Name, null);
                    }

                    killer.CorpID = corp.__ID;

                    alliance = repo.FindAlliance(killer.Alliance);
                    if (alliance == null)
                    {
                        killer.AllianceID = null;
                    }
                    else
                    {
                        killer.AllianceID = alliance.__ID;
                    }

                    invType temp = repo.FindItem(killer.Ship);
                    if (temp == null)
                    {
                        killer.ShipID = null;
                    }
                    else
                    {
                        killer.ShipID = temp.typeID;
                    }

                    temp = repo.FindItem(killer.Weapon);

                    if (temp == null)
                    {
                        killer.WeaponID = null;
                    }
                    else
                    {
                        killer.WeaponID = temp.typeID;
                    }
                   

                }
                else
                {
                    killer.PilotID = repo.FindItem(killer.Name).typeID;
                    killer.WeaponID = repo.FindItem(killer.Weapon).typeID;
                    killer.ShipID = killer.WeaponID;
                    killer.CorpID = repo.FindNPCCorp(killer.CorpName).__ID;
                }

            }

            fightSystem.SolSystemID = repo.FindSolarSystem(fightSystem.Name);


            int i = repo.SaveKill(this);

            return i;

        }

        public string GenerateText()
        {
            string crlf = "\r\n";
            StringBuilder sb = new StringBuilder();

            return sb.ToString();
        }
    }

}
